﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Windows.Browser;
using System.Text;
using System.Windows.Controls.Primitives;

namespace GreetingCreator
{
    [TemplatePart(Name = "PART_SenderName", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_SenderEmail", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_SendTo", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_SendButton", Type = typeof(Button))]
    [TemplatePart(Name = "PART_CaptchaControl", Type = typeof(CaptchaControl))]
    [TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
    [TemplateVisualState(Name = "Succeeded", GroupName = "CommonStates")]
    [TemplateVisualState(Name = "CaptchaFailed", GroupName = "CommonStates")]
    [TemplateVisualState(Name = "SendEmailFailed", GroupName = "CommonStates")]
    [TemplateVisualState(Name = "SendingGreetingMode", GroupName = "CommonStates")]
    [TemplateVisualState(Name = "ReceivingGreetingMode", GroupName = "CommonStates")]
    [TemplateVisualState(Name = "InvalidEmail", GroupName = "CommonStates")]
    [TemplateVisualState(Name = "InvalidSendTo", GroupName = "CommonStates")]
    [TemplateVisualState(Name = "ReadMode", GroupName = "PresentationStates")]
    [TemplateVisualState(Name = "EditMode", GroupName = "PresentationStates")]
    public class GreetingCreatorControl : Control, INotifyPropertyChanged
    {
        #region Fields

        private TextBox _senderName = null;
        private TextBox _senderEmail = null;
        private TextBox _sendTo = null;
        private Button _sendButton = null;
        private CaptchaControl _captchaControl = null;
        private GreetingServiceReference.GreetingData _greeting = new GreetingServiceReference.GreetingData() { Message = string.Empty, GraphicElements = new ObservableCollection<GreetingCreator.GreetingServiceReference.GraphicElementData>() };

        #endregion Fields
        #region Contructors

        public GreetingCreatorControl()
            : base()
        {
            this.DefaultStyleKey = typeof(GreetingCreatorControl);
            this.Loaded += new RoutedEventHandler(OnLoaded);            
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            this.DataContext = _greeting;
        }

        #endregion Contructors
        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #endregion INotifyPropertyChanged
        #region Properties

        public static readonly DependencyProperty SucceededMessageProperty = DependencyProperty.Register("SucceededMessage", typeof(string), typeof(GreetingCreatorControl), new PropertyMetadata(OnSucceededMessagePropertyChanged));
        /// <summary>
        /// A message that displays output when a greeting has been successfully sent to server. The message is generated from the SucceededMessageTemplate.
        /// </summary>
        public string SucceededMessage
        {
            get { return (string)GetValue(SucceededMessageProperty); }
            set { SetValue(SucceededMessageProperty, value); }
        }

        private static void OnSucceededMessagePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            GreetingCreatorControl greetingCreator = sender as GreetingCreatorControl;
            if (greetingCreator != null)
            {
                greetingCreator.NotifyPropertyChanged("SucceededMessage");
            }
        }


        public static readonly DependencyProperty SucceededMessageTemplateProperty = DependencyProperty.Register("SucceededMessageTemplate", typeof(string), typeof(GreetingCreatorControl), new PropertyMetadata(OnSucceededMessageTemplatePropertyChanged));
        /// <summary>
        /// A template for the message that will be displayed to the user when a message have been sent and saved.
        /// </summary>
        public string SucceededMessageTemplate
        {
            get { return (string)GetValue(SucceededMessageTemplateProperty); }
            set { SetValue(SucceededMessageTemplateProperty, value); }
        }

        private static void OnSucceededMessageTemplatePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            GreetingCreatorControl greetingCreator = sender as GreetingCreatorControl;
            if (greetingCreator != null)
            {
                greetingCreator.NotifyPropertyChanged("SucceededMessageTemplate");
            }
        }


        public static readonly DependencyProperty EmailSubjectProperty = DependencyProperty.Register("EmailSubject", typeof(string), typeof(GreetingCreatorControl), new PropertyMetadata(OnEmailSubjectPropertyChanged));
        /// <summary>
        /// Subject of the e-mail message that will be send to the recipients.
        /// </summary>
        public string EmailSubject
        {
            get { return (string)GetValue(EmailSubjectProperty); }
            set { SetValue(EmailSubjectProperty, value); }
        }

        private static void OnEmailSubjectPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            GreetingCreatorControl greetingCreator = sender as GreetingCreatorControl;
            if (greetingCreator != null)
            {
                greetingCreator.NotifyPropertyChanged("EmailSubject");
            }
        }


        public static readonly DependencyProperty EmailMessageTemplateProperty = DependencyProperty.Register("EmailMessageTemplate", typeof(string), typeof(GreetingCreatorControl), new PropertyMetadata(OnEmailMessageTemplatePropertyChanged));
        /// <summary>
        /// Template of the email message sent to the recipients.
        /// </summary>
        public string EmailMessageTemplate
        {
            get { return (string)GetValue(EmailMessageTemplateProperty); }
            set { SetValue(EmailMessageTemplateProperty, value); }
        }

        private static void OnEmailMessageTemplatePropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            GreetingCreatorControl greetingCreator = sender as GreetingCreatorControl;
            if (greetingCreator != null)
            {
                greetingCreator.NotifyPropertyChanged("EmailMessageTemplate");
            }
        }


        public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(GreetingCreatorControl), new PropertyMetadata(new PropertyChangedCallback(OnIsReadOnlyPropertyChanged)));
        /// <summary>
        /// Get or set if the greeting should be read only.
        /// </summary>
        public bool IsReadOnly
        {
            get { return (bool)GetValue(IsReadOnlyProperty); }
            set { SetValue(IsReadOnlyProperty, value); }
        }

        private static void OnIsReadOnlyPropertyChanged(object sender, DependencyPropertyChangedEventArgs d)
        {
            GreetingCreatorControl greetingCreator = sender as GreetingCreatorControl;
            if (greetingCreator != null)
            {
                bool isReadOnly = (bool)d.NewValue;
                if (isReadOnly)
                {
                    VisualStateManager.GoToState(greetingCreator, "ReadMode", false);
                }
                else
                {
                    VisualStateManager.GoToState(greetingCreator, "EditMode", false);
                }

                greetingCreator.NotifyPropertyChanged("IsReadOnly");
            }
        }


        public static readonly DependencyProperty UseMultipleGraphicElementsProperty = DependencyProperty.Register("UseMultipleGraphicElements", typeof(bool), typeof(GreetingCreatorControl), new PropertyMetadata(OnUseMultipleGraphicElementsPropertyChanged));
        /// <summary>
        /// Get or set if the content area can have several items.
        /// </summary>
        public bool UseMultipleGraphicElements
        {
            get { return (bool)GetValue(UseMultipleGraphicElementsProperty); }
            set { SetValue(UseMultipleGraphicElementsProperty, value); }
        }

        private static void OnUseMultipleGraphicElementsPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            GreetingCreatorControl greetingCreator = sender as GreetingCreatorControl;
            if (greetingCreator != null)
            {
                greetingCreator.NotifyPropertyChanged("UseMultipleGraphicElements");
            }
        }

        public static readonly DependencyProperty SendTextProperty = DependencyProperty.Register("SendText", typeof(string), typeof(GreetingCreatorControl), new PropertyMetadata(OnSendTextPropertyChanged));
        
        /// <summary>
        /// Get or set text that will displayed as Send-button and its tooltip.
        /// </summary>
        public string SendText
        {
            get { return (string)GetValue(SendTextProperty); }
            set { SetValue(SendTextProperty, value); }
        }

        private static void OnSendTextPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            GreetingCreatorControl control = sender as GreetingCreatorControl;
            if (control != null)
            {
                control.NotifyPropertyChanged("SendText");
            }
        }

        #endregion Properties
        #region Methods

        public GreetingServiceReference.GraphicElementData AddGraphicElement(string content, string templateName)
        {
            if (!UseMultipleGraphicElements)
            {
                _greeting.GraphicElements.Clear();
            }

            GreetingServiceReference.GraphicElementData element = new GreetingCreator.GreetingServiceReference.GraphicElementData();
            element.Content = content;
            element.TemplateName = templateName;

            _greeting.GraphicElements.Add(element);

            return element;
        }

        public bool RemoveGraphicElement(object element)
        {
            GreetingServiceReference.GraphicElementData graphicElement = element as GreetingServiceReference.GraphicElementData;
            if (graphicElement != null)
            {
                return _greeting.GraphicElements.Remove(graphicElement);
            }
            return false;
        }

        private void TryDisplayGreetingFromQuery()
        {
            if (HtmlPage.IsEnabled)
            {
                // Check if the page is called with a greeting id and if it is, try to get the greeting.
                if (HtmlPage.Document.QueryString.Keys.Contains("greetingid"))
                {
                    this.IsReadOnly = true;
                    string id = HtmlPage.Document.QueryString["greetingid"];
                    GetGreeting(id);
                }
            }
        }

        private void GetGreeting(string id)
        {
            try
            {
                Guid guid = new Guid(id);
                GetGreeting(guid);
            }
            catch (FormatException fe)
            {
                // The id string is not on the right format.
            }
        }

        private void GetGreeting(Guid id)
        {
            GreetingServiceReference.GreetingServiceClient client = new GreetingCreator.GreetingServiceReference.GreetingServiceClient("BasicHttpBinding_IGreetingService");
            client.GetGreetingCompleted += new EventHandler<GreetingCreator.GreetingServiceReference.GetGreetingCompletedEventArgs>(OnGetGreetingCompleted);

            client.GetGreetingAsync(id);

            VisualStateManager.GoToState(this, "ReceivingGreetingMode", true);
        }

        private void SendGreeting()
        {
            if (!ValidateInput())
            {
                return;
            }

            GreetingServiceReference.GreetingServiceClient client = new GreetingCreator.GreetingServiceReference.GreetingServiceClient("BasicHttpBinding_IGreetingService");
            client.SaveAndSendGreetingCompleted += new EventHandler<GreetingCreator.GreetingServiceReference.SaveAndSendGreetingCompletedEventArgs>(OnSaveAndSendGreetingCompleted);

            GreetingServiceReference.SenderInfo senderInfo = new GreetingCreator.GreetingServiceReference.SenderInfo();
            senderInfo.Name = _senderName.Text;
            senderInfo.EmailAddress = _senderEmail.Text;
            senderInfo.RecipientAddresses = GetSendToEmailAddresses();
            senderInfo.EmailSubject = EmailSubject;
            senderInfo.EmailMessage = EmailMessageTemplate;

            client.SaveAndSendGreetingAsync(_captchaControl.CaptchaAnswer, _greeting, senderInfo);

            VisualStateManager.GoToState(this, "SendingGreetingMode", true);
        }

        private bool ValidateInput()
        {
            if (!ValidateEmail(_senderEmail.Text))
            {
                VisualStateManager.GoToState(this, "InvalidEmail", true);
                return false;
            }

            ObservableCollection<string> addressList = GetSendToEmailAddresses();
            foreach (string email in addressList)
            {
                if (!ValidateEmail(email))
                {
                    VisualStateManager.GoToState(this, "InvalidSendTo", true);
                    return false;
                }
            }

            return true;
        }

        private bool ValidateEmail(string email)
        {
            Regex regex = new Regex(@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$", RegexOptions.IgnoreCase);

            if (regex.IsMatch(email))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private ObservableCollection<string> GetSendToEmailAddresses()
        {
            char[] splitters = new char[] { ',', ' ', ';' };
            string[] addresses = _sendTo.Text.Split(splitters, StringSplitOptions.RemoveEmptyEntries);
            ObservableCollection<string> addressList = new ObservableCollection<string>();
            foreach (string s in addresses)
            {
                addressList.Add(s);
            }
            return addressList;
        }

        public override void OnApplyTemplate()
        {
            this._senderName = this.GetTemplateChild("PART_SenderName") as TextBox;
            if (_senderName == null)
            {
                throw new NullReferenceException("Could not find templated PART: PART_SenderName");
            }

            this._senderEmail = this.GetTemplateChild("PART_SenderEmail") as TextBox;
            if (_senderEmail == null)
            {
                throw new NullReferenceException("Could not find templated PART: PART_SenderEmail");
            }

            this._sendTo = this.GetTemplateChild("PART_SendTo") as TextBox;
            if (_sendTo == null)
            {
                throw new NullReferenceException("Could not find templated PART: PART_SendTo");
            }

            this._sendButton = this.GetTemplateChild("PART_SendButton") as Button;
            if (_sendButton == null)
            {
                throw new NullReferenceException("Could not find templated PART: PART_SendButton");
            }
            _sendButton.Click += new RoutedEventHandler(OnSendButtonClick);

            this._captchaControl = this.GetTemplateChild("PART_CaptchaControl") as CaptchaControl;
            if (_captchaControl == null)
            {
                throw new NullReferenceException("Could not find templated PART: PART_CaptchaControl");
            }


            // Do things that will set different states. We need to do this here in OnApplyTemplate. 
            // If we do these things in OnLoaded the states will not be set.

            if (IsReadOnly)
            {
                VisualStateManager.GoToState(this, "ReadMode", false);
            }
            else
            {
                VisualStateManager.GoToState(this, "EditMode", false);
            }

            TryDisplayGreetingFromQuery();

            base.OnApplyTemplate();
        }

        #region Event handlers

        private void OnSendButtonClick(object sender, RoutedEventArgs e)
        {
            SendGreeting();
        }

        private void OnGetGreetingCompleted(object sender, GreetingCreator.GreetingServiceReference.GetGreetingCompletedEventArgs e)
        {
            GreetingServiceReference.GreetingServiceClient client = sender as GreetingServiceReference.GreetingServiceClient;

            if (client != null)
            {
                _greeting = e.Result;
                this.DataContext = _greeting;
                client.CloseAsync();

                VisualStateManager.GoToState(this, "Normal", false);
            }
        }

        private void OnSaveAndSendGreetingCompleted(object sender, GreetingCreator.GreetingServiceReference.SaveAndSendGreetingCompletedEventArgs e)
        {
            GreetingServiceReference.GreetingServiceClient client = sender as GreetingServiceReference.GreetingServiceClient;
            GreetingServiceReference.GreetingResponse response = e.Result;

            if (client != null)
            {
                _captchaControl.Reload();

                if (response.Status == GreetingServiceReference.GreetingStatus.CaptchaFailed)
                {
                    VisualStateManager.GoToState(this, "CaptchaFailed", true);
                }
                else
                {
                    // The message has been saved to server, create the output message.
                    StringBuilder message = new StringBuilder();
                    message.AppendFormat(SucceededMessageTemplate, response.Message);
                    SucceededMessage = message.ToString();

                    if (response.Status == GreetingServiceReference.GreetingStatus.Succeeded)
                    {
                        VisualStateManager.GoToState(this, "Succeeded", true);
                    }
                    else if (response.Status == GreetingServiceReference.GreetingStatus.SendEmailFailed)
                    {
                        VisualStateManager.GoToState(this, "SendEmailFailed", true);
                    }
                    else
                    {
                        VisualStateManager.GoToState(this, "Normal", true);
                    }
                }

                client.CloseAsync();
            }
        }

        #endregion Event handlers
        #endregion Methods
    }
}
